/*
* animated_sprite.cpp
*
*/
#include <fstream>
#include <string>

#include "animated_sprite.h"

using namespace std;

/*  Constructor
*
*/
AnimatedSprite::AnimatedSprite() {
  int i,j;
  for(i = 0; i < MAX_ANIMATION; ++i) {
    _seqSize[i] = 0;    
    for(j = 0; j < MAX_FRAME_PER_ANIMATION; ++j) {
      _bmpArr[i][j] = 0x00;
      _statusArr[i][j] = 0;
    }
  }
  _animationPause = 0;
  _currentAnimation = 0;
  _currentFrameTime = 0;
  _currentFrame = 0;
}
/*  Destructor
*
*/
AnimatedSprite::~AnimatedSprite() {
  int i,j;
  for(i = 0; i < MAX_ANIMATION; ++i)
    for(j = 0; j < MAX_FRAME_PER_ANIMATION; ++j)
      _bmpArr[i][j] = NULL;
}

/* Liberator
*
*/
void AnimatedSprite::__free() {
  int i,j;
  for(i = 0; i < MAX_ANIMATION; ++i)
    for(j = 0; j < MAX_FRAME_PER_ANIMATION; ++j)
      if ( _bmpArr[i][j]) destroy_bitmap(_bmpArr[i][j]);
}
/*  __updateAnimation() : set the Sprite to the next frame of the current animation
*   @return : returns 1 if it was the last frame, 0 otherwise
*/
int AnimatedSprite::__updateAnimation(int debug) {
  if ( _animationPause ) return 0;
  if ( debug ) allegro_message("%d %d %d", _currentAnimation, _currentFrame, _seqArr[_currentAnimation][_currentFrame]);
  __setBitmap(_bmpArr[_currentAnimation][_seqArr[_currentAnimation][_currentFrame]]);
  __setBoundingBox(_bbArr[_currentAnimation][_currentFrame]);
  ++_currentFrameTime;
  if ( _currentFrameTime >= _seqTimeArr[_currentAnimation][_currentFrame] ) {
    ++_currentFrame;
    _currentFrameTime = 0;
  }
  if ( _currentFrame >= _seqSize[_currentAnimation]) {
    _currentFrame = 0; // because it's a loop
    _currentFrameTime = 0;
  }
  if ( _currentFrame == 0 && _currentFrameTime == 0 ) return 1;
  if ( _statusArr[_currentAnimation][_currentFrame] != 0
  && _currentFrameTime == 0)
    return _statusArr[_currentAnimation][_currentFrame];
  return 0;
}

/* __addBitmap() : set the bitmap of a frame from a sequence
*   @arg:
*     - BITMAP * nres : Bitmap resource pointer
*     - int sequence  : sequence in which you need the Bitmap to be set
*     - int frame     : frame number you want to replace
*     - int duration  : length of the animation (in frames)
*/
void AnimatedSprite::__addBitmap(BITMAP * nres, int sequence, int frame, int duration = 1) {
  _bmpArr[sequence][frame] = nres;
  _seqTimeArr[sequence][frame] = duration;
  _bbArr[sequence][frame].__setBoundingBox(BoundingBox());
  _bbArr[sequence][frame].__enableBoundingBox(0);
}

/* __addBoundingBox() : set the bounding box of a frame from a sequence
*   @arg:
*     - BoundingBox nbb : Boundingbox resource
*     - int sequence    : sequence in which you need the Boundingbox to be attached
*     - int frame       : frame number in which the Boundingbox is going to be attached
*/
void AnimatedSprite::__addBoundingBox(BoundingBox nbb, int sequence, int frame) {
  _bbArr[sequence][frame] = nbb;
  _bbArr[sequence][frame].__enableBoundingBox(1);
}

/* __setSequence() : defines the sequence for a given animation
*   @arg:
*     - int sequence      : the animation to be changed
*     - int l             : the length of the animation
*     - int seqArr[]      : the sequence itself (e.g: {1, 2, 3, 1, 3})
*     - int seqTimeArr[]  : array having each frame's duration
*/
void AnimatedSprite::__setSequence(int sequence, int l, int seqArr[MAX_SEQUENCE_LENGTH],  int seqTimeArr[MAX_SEQUENCE_LENGTH]) {
  int i;
  _seqSize[sequence] = l;
  for(i = 0; i < l; ++i) {
    _seqArr[sequence][i] = seqArr[i];
    _seqTimeArr[sequence][i] = seqTimeArr[i];
  }
}

/* __getSequence()
*
*/
int AnimatedSprite::__getSequence() { return _currentAnimation; }
/* __changeSequence() : change the displayed sequence
*   @arg:
*     - int nseq : new sequence
*/
void AnimatedSprite::__changeSequence(int nseq) {
  // Assert
   if ( nseq >= MAX_ANIMATION ) return;
   if ( !_bmpArr[nseq][0] ) return;  

  // End
  if ( _currentAnimation != nseq ) {
    _currentFrame = 0;
    _currentFrameTime = 0;
  }
  _currentAnimation = nseq;
  __setBitmap(_bmpArr[_currentAnimation][_currentFrame]);
}

/* __resetSequence() : reset the sequence animation
*
*/
void AnimatedSprite::__resetSequence() { 
  _currentFrame = _currentFrameTime = 0;
  __setBitmap(_bmpArr[_currentAnimation][_currentFrame]);
}

/* __readFile() : Automated function to read and add every frame of every animations
*   @arg:
*     - const char * fname : path of the file to be readed
*     - int autoBB : will automatically defines bounding boxes if set to 1
*   @readme: animation_readme.txt
*/
void AnimatedSprite::__readFile(const char * fname, int autoBB) {
  static string sAnimations[MAX_ANIMATION] = {
    "ANIMATION_1", "ANIMATION_2", "ANIMATION_3", "ANIMATION_4",
    "ANIMATION_5", "ANIMATION_6"
  };

  ifstream animationFile; // Read only
  string sbuffer;
  int ibuffer, errCatch;
  int sequence, getSequence, seqLength, numFrames, i;
  char file_path[64], tmp_path[64];


  BITMAP * tmp[MAX_FRAME_PER_ANIMATION];

  animationFile.open(fname);
  if ( !animationFile.is_open()) {
    allegro_message("Error could not open %s",fname);
    exit(-1);
  }

  sbuffer = fname; // Let's get the path
  animationFile >> sbuffer;
  strcpy(file_path, sbuffer.c_str());



  while(!animationFile.eof()) {
    getSequence = 0, errCatch = 1;
    animationFile >> sbuffer;
    /*
    * Part 1: getting the sequence name
    */
    while(getSequence < MAX_ANIMATION) {  // So we know the sequence
      if (sAnimations[getSequence] == sbuffer) {
        sequence = getSequence;
        getSequence = MAX_ANIMATION;
        errCatch = 0;
      }
      else ++getSequence;
    } 
    if ( errCatch ) {  // An error let's stop here
      allegro_message("Error #01 - Sequence not found: %s",sbuffer.c_str());
      exit(-1);
    }
    /*
    * Part 2: getting the sequence length
    */
    animationFile >> numFrames;
    if ( numFrames < 1 || numFrames > MAX_FRAME_PER_ANIMATION ) {
      allegro_message("Error #02 - Too many/not enough frame(s) for the sequence %s", sbuffer.c_str());
      exit(-1);
    }
    animationFile >> seqLength;
    if ( seqLength < 1 || seqLength > MAX_SEQUENCE_LENGTH ) {
      allegro_message("Error #03 - Sequence Length is way too high or null for the sequence %s", sbuffer.c_str());
      exit(-1);
    }
    /*
    * Part 3: buffering each frames of the sequence and setting up datas
    */
    _seqSize[sequence] = seqLength;
    for(i = 0; i < numFrames; ++i) {
      animationFile >> sbuffer;
      strcpy(tmp_path, file_path);
      strcat(tmp_path, sbuffer.c_str());
      tmp[i] = load_bitmap(tmp_path, NULL);
      if ( !tmp[i] ) {
        allegro_message("Error #04 - Could not load bitmap %s", sbuffer.c_str());
        exit(-1);
      }
      __addBitmap(tmp[i], sequence, i);            
    }
    /*
    * Part 4: creating the sequence
    */
    for(i = 0; i < seqLength; ++i) {
      animationFile >> ibuffer;
      _seqArr[sequence][i] = ibuffer;
      //if ( autoBB )
      //  __addBoundingBox(BoundingBox(0, 0, tmp[ibuffer]->w, tmp[ibuffer]->h), sequence, i);
    }
    /*
    * Part 5: recording lengths
    */
    for(i = 0; i < seqLength; ++i) {
     animationFile >> ibuffer;
     if ( ibuffer == 0 ) ibuffer = 1;
     _seqTimeArr[sequence][i] = ibuffer;
    }
    /*
    * Part 6: clearing
    */
    sequence = 0;
    seqLength = 0;
    numFrames = 0;
    getSequence = 0;
  }
  animationFile.close();
}
/*
*
*/
void AnimatedSprite::__copyPack(AnimatedSprite * nPack) {
 int i,j;
  for(i = 0; i < MAX_ANIMATION; ++i)
    for(j = 0; j < MAX_FRAME_PER_ANIMATION; ++j)
      _bmpArr[i][j] = nPack->_bmpArr[i][j];
  for(i = 0; i < MAX_ANIMATION; ++i) {
    _seqSize[i] = nPack->_seqSize[i];     
    for(j = 0; j < MAX_SEQUENCE_LENGTH; ++j) {
      _seqArr[i][j] = nPack->_seqArr[i][j];
      _seqTimeArr[i][j] = nPack->_seqArr[i][j];
    }
  }
}
/* __linkStatus() : allows __updateAnimation() to return a special integer
*   @arg:
*     - int status : integer that will be returned via __updateAnimation()
*     - int sequence : sequence that will return status
*     - int frame : frame that will return status
*/
void AnimatedSprite::__linkStatus(int status, int sequence, int frame) {
  _statusArr[sequence][frame] = status;
}
